﻿using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using TiffLibrary;

namespace WholeSlideImageViewer.TiffExport
{
    internal class StreamContentReaderWriter : TiffFileContentReaderWriter
    {
        private readonly Stream _stream;
        private readonly SemaphoreSlim _semaphore;

        public StreamContentReaderWriter(Stream stream)
        {
            _stream = stream;
            _semaphore = new SemaphoreSlim(1);
        }

        public override void Flush()
        {
            _semaphore.Wait();
            try
            {
                _stream.Flush();
            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override int Read(TiffStreamOffset offset, ArraySegment<byte> buffer)
        {
            Stream? stream = _stream;
            if (offset.Offset > stream.Length)
            {
                return default;
            }
            if (buffer.Array is null)
            {
                return 0;
            }

            _semaphore.Wait();
            try
            {
                stream.Seek(offset.Offset, SeekOrigin.Begin);
                return stream.Read(buffer.Array, buffer.Offset, buffer.Count);
            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override int Read(TiffStreamOffset offset, Memory<byte> buffer)
        {
            Stream? stream = _stream;
            if (offset.Offset > stream.Length)
            {
                return default;
            }

            _semaphore.Wait();
            try
            {
                stream.Seek(offset.Offset, SeekOrigin.Begin);
                return stream.Read(buffer.Span);
            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override async ValueTask<int> ReadAsync(TiffStreamOffset offset, ArraySegment<byte> buffer, CancellationToken cancellationToken = default)
        {
            Stream? stream = _stream;
            if (offset.Offset > stream.Length)
            {
                return 0;
            }
            if (buffer.Array is null)
            {
                return 0;
            }

            await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                stream.Seek(offset.Offset, SeekOrigin.Begin);
                return await stream.ReadAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken).ConfigureAwait(false);
            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override async ValueTask<int> ReadAsync(TiffStreamOffset offset, Memory<byte> buffer, CancellationToken cancellationToken = default)
        {
            Stream? stream = _stream;
            if (offset.Offset > stream.Length)
            {
                return default;
            }

            await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                stream.Seek(offset.Offset, SeekOrigin.Begin);
                return await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override void Write(TiffStreamOffset offset, ReadOnlyMemory<byte> buffer)
        {
            _semaphore.Wait();
            try
            {
                _stream.Seek(offset.ToInt64(), SeekOrigin.Begin);
                _stream.Write(buffer.Span);

            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override void Write(TiffStreamOffset offset, ArraySegment<byte> buffer)
        {
            _semaphore.Wait();
            try
            {
                _stream.Seek(offset.ToInt64(), SeekOrigin.Begin);
                _stream.Write(buffer.Array!, buffer.Offset, buffer.Count);

            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override async ValueTask WriteAsync(TiffStreamOffset offset, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
        {
            await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                _stream.Seek(offset.ToInt64(), SeekOrigin.Begin);
                await _stream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
            }
            finally
            {
                _semaphore.Release();
            }
        }

        public override async ValueTask WriteAsync(TiffStreamOffset offset, ArraySegment<byte> buffer, CancellationToken cancellationToken = default)
        {

            await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                _stream.Seek(offset.ToInt64(), SeekOrigin.Begin);
                await _stream.WriteAsync(buffer.Array!, buffer.Offset, buffer.Count, cancellationToken).ConfigureAwait(false);
            }
            finally
            {
                _semaphore.Release();
            }
        }

        protected override void Dispose(bool disposing) { }
    }
}
